package com.yvan.serverless.jdbc;

import com.google.common.collect.Lists;
import com.yvan.Conv;
import com.yvan.serverless.mybatis.MapperWatcher;
import com.yvan.serverless.utils.ScriptUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyArray;
import org.graalvm.polyglot.proxy.ProxyObject;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class DaoFunctions implements InitializingBean {

    public static DaoFunctions INSTANCE;

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    @Autowired
    private DaoResultTransformService daoResultTransformService;

    @Override
    public void afterPropertiesSet() throws Exception {
        INSTANCE = this;
    }

    public int insertAny(String tableName, Value paramsRaw) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            Map<String, Object> fieldAndValue = (Map<String, Object>) ScriptUtils.toValue(paramsRaw);
            return new DaoExecuter(sqlSession).insertAny(tableName, fieldAndValue);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public int insertAnyBatch(String tableName, Value paramsRaw) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            List<Map<String, Object>> fieldAndValueList = (List<Map<String, Object>>) ScriptUtils.toValue(paramsRaw);
            return new DaoExecuter(sqlSession).insertAnyBatch(tableName, fieldAndValueList);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public int updateAny(String tableName, Value setRaw, Value whereRaw) {
        Map<String, Object> sets = (Map<String, Object>) ScriptUtils.toValue(setRaw);
        Map<String, Object> wheres = (Map<String, Object>) ScriptUtils.toValue(whereRaw);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            return new DaoExecuter(sqlSession).updateAny(tableName, sets, wheres);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public int save(String sqlId, Value paramsRaw) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }
        Map<String, Object> params = (Map<String, Object>) ScriptUtils.toValue(paramsRaw);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            return sqlSession.update(sqlId, params);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public int deleteAny(String tableName, Value paramsRaw) {
        Map<String, Object> wheres = (Map<String, Object>) ScriptUtils.toValue(paramsRaw);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            return new DaoExecuter(sqlSession).deleteAny(tableName, wheres);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public int deleteByIds(String tableName, String idField, List<Object> ids) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            return new DaoExecuter(sqlSession).deleteByIds(tableName, idField, ids);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public int remove(String sqlId, Value paramsRaw) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }

        Object params = ScriptUtils.toValue(paramsRaw);

        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            return sqlSession.delete(sqlId, params);
        } finally {
            sqlSession.close();//关闭资源
        }
    }

    public Object selectListAny(String sqlId) throws Exception {
        return selectListAny(sqlId, null);
    }

    public Object selectListAny(String sqlId, Value paramsRaw) throws Exception {
        if (MapperWatcher.getXmlException() != null) {
            throw MapperWatcher.getXmlException();
        }

        Object params = ScriptUtils.toValue(paramsRaw);

        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            List<Map<String, Object>> result = sqlSession.selectList(sqlId, params);
            convert(result);

            boolean isExportData = Conv.asBoolean(((Map) params).get("isExportData"));
            if (isExportData) {
                return convertExport(result);
            }
            return toAny(result);

        } finally {
            sqlSession.close();//关闭资源
        }
    }

    private List<List<?>> convertExport(List<Map<String, Object>> result) {

        List<List<?>> newResult = Lists.newArrayListWithCapacity(result.size() + 1);
        List<String> keys =  new ArrayList<>(result.get(0).keySet());
        newResult.add(keys);
        for (Map<String, Object> it : result) {
            List<Object> values = Lists.newArrayListWithCapacity(keys.size() + 1);
            for (String key : keys) {
                values.add(it.get(key));
            }
            newResult.add(values);
        }
        return newResult;
    }

    private void convert(List<Map<String, Object>> result) {
        for (Map<String, Object> row : result) {
            daoResultTransformService.convert(row);
        }
    }

    private ProxyArray toAny(List<Map<String, Object>> result) {
        Object[] objs = new Object[result.size()];
        int i = 0;
        for (Map<String, Object> row : result) {
            if (row != null) {
                objs[i++] = ProxyObject.fromMap(row);
            }
        }
        return ProxyArray.fromArray(objs);
    }

}
